package middle_man;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

import config.Config;
import config.MiddleManConfiguration;

public class SetupThread extends Thread {
	private ServerSocket serverSocket;
	private ClientsAccepter ca;
	private SmartQueue sq;
	
	public SetupThread() {
		ca = null;
		sq = null;
		try {
			serverSocket = new ServerSocket(Config.middle_man_setup_port);
		} catch (IOException e) {
			System.err.println("Could not listen on port "+Config.middle_man_setup_port);
			e.printStackTrace();
		}
	}
	
	private void killThreads() {
		// Stop all threads if they are still running
		if(ca != null) {
			// Close the socket the clients accepter is waiting for
			ca.terminate();
			try {
				ca.getServerSocket().close();
			} catch (IOException e1) {
				// Don't do anything, this is mean code
			}
			System.err.println("SetupThread: Waiting for Clients Accepter to join with all its threads...");
			try {
				ca.join();
			} catch(InterruptedException e) {
				System.err.println("SetupThread: ");
				e.printStackTrace();
			}
			System.err.println("SetupThread: Clients Accepter has joined... Yuppy");
			ca = null;
		}
		if(sq != null) {
			System.err.println("SetupThread: Waiting for Smart Queue to join with all its threads...");
			sq.terminate();
			sq = null;
			System.err.println("SetupThread: Smart Queue has joined... Yuppy");
		}
		
		
		
		
		// Find the root thread group
	    ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
	    while (root.getParent() != null) {
	        root = root.getParent();
	    }
	    
	    System.err.println("RUNNING THREADS:");
	    // Visit each thread group
	    visit(root, 0);
	}
	
	public void run() {
		this.setName("MiddleMan - SetupThread");
		String fromClient;
		System.err.println("SetupThread: running ...");
		while(true) {
			Socket clientSocket = null;
			try {
				clientSocket = serverSocket.accept();
			} catch(IOException e) {
				System.err.println("SetupThread: New client accept failed");
				e.printStackTrace();
				return;
			}
			
			System.err.println("SetupThread: New client accepted...");
			
			BufferedReader in;
			PrintWriter out;
			try {
				in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				out = new PrintWriter(clientSocket.getOutputStream());
				do {
					fromClient = in.readLine();
				} while(fromClient == null);
				System.err.println("SetupThread: Received configuration: "+fromClient);
				
				if(fromClient.startsWith("EXPERIMENT")) {
					runExperiment(fromClient);
				} else if(fromClient.startsWith("TERMINATE")) {
					killThreads();
					out.println("OK");
					out.println("OK");
					out.println("OK");
				}
				in.close();
				out.close();
			} catch (IOException e) {
				System.err.println("SetupThread: Can't read from client...");
				e.printStackTrace();
			}
		}
	}

	private void runExperiment(String fromClient) {
		fromClient = fromClient.substring(10);
		killThreads();
		System.err.println("STARTING middle man");
		
		// Try to parse the received parameters
		try {
			MiddleManConfiguration receivedConfig = new MiddleManConfiguration(fromClient);
			sq = new SmartQueue(receivedConfig.P(), receivedConfig.L(), receivedConfig.M(), receivedConfig.databases());
			sq.startWorkerThreads();
			ca = new ClientsAccepter(sq);
			ca.start();
		} catch(IllegalArgumentException e) {
			System.err.println("SetupThread: Failed to start middle man: " + e.getMessage());
		}
	}
	
	
	// This method recursively visits all thread groups under `group'.
    public static void visit(ThreadGroup group, int level) {
        // Get threads in `group'
        int numThreads = group.activeCount();
        Thread[] threads = new Thread[numThreads*2];
        numThreads = group.enumerate(threads, false);
    
        // Enumerate each thread in `group'
        for (int i=0; i<numThreads; i++) {
            // Get thread
            Thread thread = threads[i];
            String name = thread.getName();
            if(name.startsWith("MiddleMan")) {
            	System.err.println(" - " + name);
            }
        }
    
        // Get thread subgroups of `group'
        int numGroups = group.activeGroupCount();
        ThreadGroup[] groups = new ThreadGroup[numGroups*2];
        numGroups = group.enumerate(groups, false);
    
        // Recursively visit each subgroup
        for (int i=0; i<numGroups; i++) {
            visit(groups[i], level+1);
        }
    }
}
